package gui.table;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import models.DatabaseModel;
import models.Searchable;

/**
 * Helper class that filters, searches and sorts a list
 * by providing one method: transform.
 * 
 * How the list is transformed is controlled by setting
 * the properties of an instance during it's lifetime,
 * with the setter methods (searchString and filter).
 *
 * @param <Model>
 */
public class ListTransformer <Model extends DatabaseModel> {
	private SearchFilter searchFilter;
	private Filter<Model> itemFilter;
	
	private class SearchFilter implements Filter<Model> {
		private List<String> searchTerms = new ArrayList<String>();
		
		/**
		 * Create a new searchfilter. The searchTerms string may have multiple
		 * parts, separated by commas (and possibly surrounded by whitespace).
		 * @param searchTermsString
		 */
		public SearchFilter(String searchTermsString) {
			String[] searchTerms = searchTermsString.split(",");
			for(String searchTerm : Arrays.asList(searchTerms)) {
				this.searchTerms.add(searchTerm.trim());
			}
		}
		
		/**
		 * Check whether the model matches all search terms.
		 * @param model to check
		 */
		@Override
		public boolean isAllowed(Model model) {
			Searchable objToMatch = (Searchable) model;
			for (String searchTerm : searchTerms) {
				if ( ! objToMatch.isSearchMatch(searchTerm)) {
					return false;
				}
			}
			return true;
		}
	}
	
	/**
	 * Transform the given list of models, by filtering (as set by setFilter()) 
	 * and searching (as set by setSearch()). A new list will be created created.
	 * @param content List to transform
	 * @return transformed (new) list
	 */
	public List<Model> transform(List<Model> content) {
		content = new ArrayList<Model>(content);
		content = applyFilter(itemFilter, content);
		content = applyFilter(searchFilter, content);
		return content;
	}
	
	/**
	 * Filter a list according to the given filter. 
	 * @param content: list to filter 
	 * @return Filtered list
	 */
	private List<Model> applyFilter(Filter<Model> filter, List<Model> content) {
		if (filter == null) {
			return content;
		}
		List<Model> selected = new ArrayList<Model>();
		for (Model model : content) {
			if (filter.isAllowed(model)) {
				selected.add(model);
			}
		}
		return selected;
	}
	
	/**
	 * Set the search term(s). The searchString may have multiple
	 * parts, separated by commas (and possibly surrounded by whitespace).
	 * @param searchString
	 */
	public void setSearch(String searchString) {
		this.searchFilter = new SearchFilter(searchString);
	}
	
	/**
	 * Cancel the search.
	 */
	public void resetSearch() {
		this.searchFilter = null;
	}
	
	/**
	 * Set a filter to apply.
	 * @param filter
	 */
	public void setFilter(Filter<Model> filter) {
		this.itemFilter = filter;
	}
	
	/**
	 * Cancel the filter.
	 */
	public void resetFilter() {
		itemFilter = null;
	}
}
